package com.sensetime.gateway.filter;

import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.UUID;

import static com.sensetime.jv.constants.CustomConstant.REQUEST_ID;
import static com.sensetime.jv.constants.CustomConstant.REQUEST_ID_HEADER;

/**
 * 全局公共过滤器：
 *   1. 生成/透传 requestId
 *   2. 写 Reactor Context -> Log4j2 ThreadContext
 *   3. 打印 access log
 */
@Log4j2
@Component
public class GlobalCommonFilter implements GlobalFilter, Ordered {



    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {


        ServerHttpRequest request = exchange.getRequest();

        // 1. 优先取前端透传，否则生成
        String requestId = UUID.randomUUID().toString();

        // 2. 把 requestId 放到 request header 中向下游透传
        ServerWebExchange newExchange = exchange.mutate()
                .request(r -> r.header(REQUEST_ID_HEADER, requestId))
                .build();

        // 3. 放入 Reactor Context，让 %X{requestId} 生效
        return chain.filter(newExchange)
                .contextWrite(ctx -> ctx.put(REQUEST_ID, requestId))

                // 4. 前置日志
                .doFirst(() -> {
                    ThreadContext.put(REQUEST_ID, requestId);
                    log.info("==> {} {} from {}", request.getMethod(), request.getURI(), request.getRemoteAddress());
                })

                // 5. 后置日志 & 清理
                .doFinally(signal -> {
                    newExchange.getResponse().getHeaders().getDate();
                    log.info("<== {} {} cost {} ms", request.getMethod(), request.getURI(),
                            System.currentTimeMillis() - newExchange.getResponse().getHeaders().getDate());
                    ThreadContext.remove(REQUEST_ID);
                });
    }

    /** 优先级最高，越早执行越好 */
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}